/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: cbus_transport_ops.h
 *
 * Purpose: communication transport layer component API define
 *
 * Developer:
 *   wen.gu , 2021-12-23
 *
 * TODO:
 *
 ***************************************************************************/

/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __CBUS_BUS_TRANSPORT_H__
#define __CBUS_BUS_TRANSPORT_H__

#include "cbus/core/cbus_type.h"
#include "cbus/core/cbus_error.h"

/******************************************************************************
 **    MACROS
 ******************************************************************************/
/**
 *  we define the version as hex number, the high order byte aways as hex
 * 43 54 4F : 'C' 'T' 'O' (mean cbus transport ops).  just increase low order byte.
 * e.g the next version CBUS_TRANSPORT_V1 maybe 0x43544F01
*/
#define CBUS_TRANSPORT_V0 0x43544F00

#define CBUS_TRANSPORT_VERSION CBUS_TRANSPORT_V0


#define CBUS_TRANSPORT_RECEIVE_WAIT_FOREVER (0)
#define CBUS_TRANSPORT_CONNECT_WAIT_FOREVER (0)
#define CBUS_TRANSPORT_ACCEPT_WAIT_FOREVER (0)
/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
G_C_HEADER_START

typedef struct _cbus_pipe_ops
{
    /**
     * @brief destroy a pipe instance
     * @param handle [in] the isntance handle of pipe
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*finish)(GPHD handle);

    /**
     * @brief send data to remote side
     * @param handle [in] the isntance handle of pipe
     * @param buf [in] data buffer
     * @param len [in] data length
     * @return G_OK: success, else: failed @see GErrc 
     * 
     */
    GErrc (*send)(GPHD handle, GBUF buf, GU32 len);

    /**
     * @brief receive data from remote side
     * @param handle [in] the isntance handle of pipe
     * @param buf [in] the data buffer to storage received data
     * @param buf_size [in] the size of data buffer(indicat max receive size)
     * @param timeout_ms [in] how many time to wait receive. if receive nothing in 'timeout_ms', then return G_ErrTimeout, 
     *                        CBUS_TRANSPORT_RECEIVE_WAIT_FOREVER for wait forever
     * @return > 0 : receive data size, else(<= 0): error code @see GErrc
     * 
     */

    GS32 (*recv)(GPHD handle, GBUF buf, GU32 buf_size, GU32 timeout_ms);

    /** todo, add send_async, recv_async */

}cbus_pipe_ops;


/**
 * @biref define the Operations(ops) for dialer(client) side
*/
typedef struct _cbus_dialer_ops
{
    /**
     * @brief create and initialize a dialer instance
     * @param handle [out] a instance of dialer
     * @return GErrc, success: G_OK, else error code.
     * 
    */
    GErrc (*initialize)(GPHD* dialer_handle);

    /**
     * @brief destroy(frees resources associated with the dialer) a dialer instance
     * @param handle [in] a instance of dialer
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*finish)(GPHD dialer_handle);

    /**
     * @brief connect to a listener(service)
     * @param handle     [in] a instance of dialer
     * @param url        [in] the url adress of listener(service)
     * @param timeout_ms [in] how many time to wait connect to a listener. if not connect to a listener in 'timeout_ms', then return G_ErrTimeout, 
     *                        CBUS_TRANSPORT_CONNECT_WAIT_FOREVER for wait forever
     * @param pipe_handle [out] if success output pipe instance handle, esle NULL 
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*connect)(GPHD dialer_handle, const GSTR url, GU32 timeout_ms, GPHD* pipe_handle);

#if 0 /** to do, refine me?? */
    /**
     * @brief connect to a listener(service) with async mode,if successfully connect to a listener then, call the 'on_connect' callback
     * @param handle [in] a instance of dialer
     * @param url    [in] the url adress of listener(service)
     * @param on_connect [in] the callback function to notify observer we successfully connect to the listener(service)
     * @return success: return connect handler, esle: NULL
     * @note dialer will remove the 'on_connect' when successfully.
     * 
     */
    GPHD (*connect_async)(GPHD dialer_handle, const GSTR url, void(*on_connect)(GPTR opaque, GPHD pipe_handle), GPTR opaque);

    /**
     * @brief cancel async connect(remove connect handler from wait queue)
     * @param handle         [in] a instance of dialer
     * @param connect_handle [in] a 'registered' connect handler with API 'connect_async'
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*connect_async_cancel)(GPHD dialer_handle, GPHD connect_handle);
#endif

}cbus_dialer_ops;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief define the operations(ops) set for listener(service) side
 * 
 */
typedef struct _cbus_listener_ops
{
    /**
     * @brief create and initialize a listener instance
     * @param handle [out] the instance of listener(service)
     * @param opaque [in] a oapque handle of user set to listener for callback function
     * @param on_dialer_status [in] a callback to observe the status of dialer which connec/disconnect
     *                          to/from current listener
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*initialize)(GPHD* listener_handle);

    /**
     * @brief destroy a listener instance 
     * @param handle [in] the instance handle of listener
     * @return GErrc, success: G_OK, else error code.
     *  
     */
    GErrc (*finish)(GPHD listener_handle);

    /**
     * @brief offer a listener(service) with url address 
     * @param handle [in] the instance handle of listener
     * @param url [in] the url address of a listener(service)
     * @return GErrc, success: G_OK, else error code.
     */
    GErrc (*bind)(GPHD listener_handle, GSTR url);

    /**
     * @brief stop a listner(service)
     * @param handle [in] the instance handle of listener
     * @return GErrc, success: G_OK, else error code.
     */
    GErrc (*unbind)(GPHD listener_handle);

    /**
     * @brief accept a connected dialer
     * @param handle [in] the instance handle of listener
     * @param dialer_id [out] the id of dialer which connected to current listener
     * @param timeout_ms [in] the time of timeout in milli-second, if not accept a dialer in 'timeout_ms', then return G_ErrTimeout, 
     *                        CBUS_TRANSPORT_ACCEPT_WAIT_FOREVER for wait forever
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*accept)(GPHD listener_handle, GPHD* pipe_handle, GU32 timeout_ms);

}cbus_listener_ops;


typedef struct _cbus_transport_ops
{
    GU32 version;
    const GSTR scheme_name;

    /**
     * @brief if not NULL, is called once during library initialization
     * @param none
     * @return GErrc, success: G_OK, else error code.
     * 
    */
    GErrc (*initialize)();

    /**
     * @brief if not NULL, is called during library deinitialization,
     *        It should release any global resources, close any open files, etc.
     * @param none
     * @return GErrc, success: G_OK, else error code.
     * 
     */
    GErrc (*finish)();

    const cbus_dialer_ops* dialer;
    const cbus_listener_ops* listener;
    const cbus_pipe_ops* pipe;
}cbus_transport_ops;

//////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////

/** the scheme name type of transport */
typedef GCHR cbus_transport_scheme_t[32];

/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/
/**
 * @brief initislize the default transports
 * 
 * @return success: G_OK, else: error code @see GErrc
 */
G_API GErrc cbus_transport_initialize(void);

/**
 * @brief uninitialize transport
 * @param void none
 * @return void
 */
G_API void cbus_transport_finish(void);

/**
 * @brief find a valid transport class with name
 * 
 * @param trans_name [in] the name of transport
 * @return cbus_transport_ops* the reference of a transport class
 */
G_API const cbus_transport_ops* cbus_transport_find(const GSTR trans_name);

/**
 * @brief register a transport class dynamicly
 * 
 * @param cbus_trans [in] a reference of a transport class
 * @return success: G_OK, else error coce @see GErrc
 */
G_API GErrc cbus_transport_register(const cbus_transport_ops* cbus_trans);


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/** the url syntax: 
 * scheme:[//[userinfo@]host][/]path[?query][#fragment]
 * 
 * Note: here we just need prefix 'scheme'
*/

G_API GErrc cbus_transport_get_scheme_name_by_url(const GSTR url, cbus_transport_scheme_t scheme);


G_C_HEADER_END

#endif /** !__CBUS_BUS_TRANSPORT_H__ */

